package icasue.runninginvoke.task.redis;

import icasue.base.utils.check.CommonCheck;
import icasue.base.utils.wareable.FieldConvertUtil;
import lombok.Data;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/11/11 下午2:48
 * @UpdateDate:
 * @Description:
 */
@Data
public class RedisTaskOperate extends AbstractRedisAsyncTask{

    Object jedisPool;
    MethodHandle getResourceHandle;
    MethodHandle isConnectedHandle;
    MethodHandle closeResourceHandle;
    MethodHandle evalHandle;
    public static final MethodHandles.Lookup lookup = FieldConvertUtil.methodLookup;

    public RedisTaskOperate(Object jedisPool,String hashKeyHome){
        super();
        this.jedisPool = jedisPool;
        if(!CommonCheck.nullStrings(hashKeyHome)){
            super.hashKeyHome = hashKeyHome;
        }
        init();
    }

    public RedisTaskOperate(Object jedisPool){
        this(jedisPool,null);
    }

    @Override
    protected void init(){
        Class<?> jedisPoolClassMetadata = this.jedisPool.getClass();
        Class<?> jedisClassMetadata;
        try {
            //high level jedis client such as 3.1.0 we suggested.
            jedisClassMetadata = Class.forName("redis.clients.jedis.Jedis");
        }catch (Throwable e){
            // low level jedis client 2.x.x, such as 2.8.0
            try {
                jedisClassMetadata = Class.forName("redis.clients.jedis.Jedis");
            }catch (Throwable e2){
                throw new RuntimeException("RedisTaskOperate :: {} Con't read Jedis's class metadata, we suggest used 2.xxx or 3.xxx version.");
            }
        }
        try {
            //Getting method handle for get jedis resource from jedisPool.
            getResourceHandle = lookup.findVirtual(jedisPoolClassMetadata,"getResource", MethodType.methodType(jedisClassMetadata));
            //Getting method handle for close jedis resource actually return the resource to pool.
            closeResourceHandle = lookup.findVirtual(jedisClassMetadata,"close",MethodType.methodType(void.class));
            //Getting method handle for check if current jedis is connected state.
            isConnectedHandle = lookup.findVirtual(jedisClassMetadata,"isConnected",MethodType.methodType(boolean.class));
            //Getting method handle for jedis invoke lua script by eval method.
            evalHandle = lookup.findVirtual(jedisClassMetadata,"eval", MethodType.methodType(Object.class,
                    Arrays.asList(String.class,int.class, String[].class)));
        }catch (Throwable e){
            throw new RuntimeException("RedisTaskOperate :: {} Can't find core handle for jedis invoke, we suggest used 2.xxx or 3.xxx version.");
        }
    }

    @Override
    public Boolean touchExecute(String taskKey) {
        Object jedis = null;
        try {
            jedis = getResourceHandle.bindTo(jedisPool).invoke();
            Object touchResult = evalHandle.bindTo(jedis).invoke(TOUCH_EXECUTE_LUA, 2, new String[]{this.hashKeyHome, taskKey, "1"});
            if(touchResult.equals(Long.valueOf(1))){
                return Boolean.TRUE;
            }
        }catch (Throwable e){
            throw new RuntimeException("RedisTaskOperate :: touchExecute :: execute occur an error.",e.getCause());
        }finally {
            try {
                Object connectionState = isConnectedHandle.bindTo(jedis).invoke();
                if(Boolean.TRUE.equals(connectionState)){
                    closeResourceHandle.bindTo(jedis).invoke();
                }
            }catch (Throwable e){
                throw new RuntimeException("RedisTaskOperate :: touchExecute close jedis resource occur an error.");
            }
        }
        return Boolean.FALSE;
    }

    @Override
    public String getExecute(String taskKey) {
        Object jedis = null;
        try {
            jedis = getResourceHandle.bindTo(jedisPool).invoke();
            Object getResult = evalHandle.bindTo(jedis).invoke(GET_EXECUTE_LUA, 2, new String[]{this.hashKeyHome, taskKey});
            return (String)getResult;
        }catch (Throwable e){
            throw new RuntimeException("RedisTaskOperate :: getExecute :: execute occur an error.",e.getCause());
        }finally {
            try {
                Object connectionState = isConnectedHandle.bindTo(jedis).invoke();
                if(Boolean.TRUE.equals(connectionState)){
                    closeResourceHandle.bindTo(jedis).invoke();
                }
            }catch (Throwable e){
                throw new RuntimeException("RedisTaskOperate :: getExecute close jedis resource occur an error.");
            }
        }
    }

    @Override
    public Boolean destroyExecute(String taskKey) {
        Object jedis = null;
        try {
            jedis = getResourceHandle.bindTo(jedisPool).invoke();
            Object touchResult = evalHandle.bindTo(jedis).invoke(DESTROY_EXECUTE_LUA, 2, new String[]{this.hashKeyHome, taskKey});
        }catch (Throwable e){
            throw new RuntimeException("RedisTaskOperate :: touchExecute :: execute occur an error.");
        }finally {
            try {
                Object connectionState = isConnectedHandle.bindTo(jedis).invoke();
                if(Boolean.TRUE.equals(connectionState)){
                    closeResourceHandle.bindTo(jedis).invoke();
                }
            }catch (Throwable e){
                throw new RuntimeException("RedisTaskOperate :: destroyExecute close jedis resource occur an error.");
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean updateIdentity(String taskKey, String identity) {
        Object jedis = null;
        try {
            jedis = getResourceHandle.bindTo(jedisPool).invoke();
            Object updateIdentityResult = evalHandle.bindTo(jedis).invoke(UPDATE_TASK_IDENTITY, 2, new String[]{this.hashKeyHome, taskKey,identity});
        }catch (Throwable e){
            throw new RuntimeException("RedisTaskOperate :: updateIdentity :: execute occur an error.");
        }finally {
            try {
                Object connectionState = isConnectedHandle.bindTo(jedis).invoke();
                if(Boolean.TRUE.equals(connectionState)){
                    closeResourceHandle.bindTo(jedis).invoke();
                }
            }catch (Throwable e){
                throw new RuntimeException("RedisTaskOperate :: updateIdentity close jedis resource occur an error.");
            }
        }
        return Boolean.TRUE;
    }

    @Override
    public void clear() {
        Object jedis = null;
        try {
            jedis = getResourceHandle.bindTo(jedisPool).invoke();
            Object clearPreviousTaskIdentityResult = evalHandle.bindTo(jedis).invoke(CLEAR_TASK_IDENTITY, 1, new String[]{this.hashKeyHome});
        }catch (Throwable e){
            throw new RuntimeException("RedisTaskOperate :: clear :: execute occur an error.");
        }finally {
            try {
                Object connectionState = isConnectedHandle.bindTo(jedis).invoke();
                if(Boolean.TRUE.equals(connectionState)){
                    closeResourceHandle.bindTo(jedis).invoke();
                }
            }catch (Throwable e){
                throw new RuntimeException("RedisTaskOperate :: clear close jedis resource occur an error.");
            }
        }
    }
}
